/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ems.ui;

import ems.controller.Engine;
import ems.controller.PasswordField;
import ems.lib.Resource;
import ems.lib.Staff;
import ems.lib.Task;
//import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

/**
 *
 * @author hoa
 */
public class TextUI {

    private static final int MAX_LOGINS = 3;
    private static String message = "";
    public static Scanner cin = new Scanner(System.in);

    public TextUI() {
    }

    ;

    /**
     * Clear terminal screen
     */
    public static void clearScreen() {        
        if (System.getProperty("os.name").startsWith("Window")) {
            //No clearscreen for windows, no comment really :(
            //System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");            
        } else {
            System.out.print("\033c");
        }
    }

    /**
     * Print out the welcome screen
     */
    public static void displayWelcomeScreen() {
        System.out.println("===============================================================================");
        System.out.println("=                  WELCOME TO EMERGENCY MANAGEMENT SYSTEM                     =");
        System.out.println("===============================================================================");
        System.out.println("Enter 'quit' at the prompt to exit.");
        System.out.println("Please enter login information to proceed.\n\n");
    }

    /**
     * Display the login screne
     */
    public static void displayLoginScreen() {
        //boolean auth = false;
        //int count = 0;
        //do {        } while (!auth && ++count < MAX_LOGINS);
        try {
            do {
                System.out.println("o-----------------------------------------------------------------------------o");
                System.out.println("|                                LOG IN                                       |");
                System.out.println("o-----------------------------------------------------------------------------o");
                System.out.print("Username: ");
                String username = cin.nextLine();
                if (username.equals("quit")) {
                    Engine.shutdown();
                }
                //System.out.print("Password: ");
                //String password = cin.nextLine();
                String password = PasswordField.readPassword("Password: ");
                if (password.equals("quit")) {
                    Engine.shutdown();
                }
                Engine.login(username, password);
            } while (true);
        } catch (Exception e) {
            System.err.println("Database not found!");
            e.printStackTrace();
        }

    }

    /**
     * Display login failure
     */
    public static void displayLoginFail() {
        System.out.println("\n\n*** Invalid Login. Please re-enter your information ***\n");
    }

    /**
     * Display a specific shell depending on staff's role
     * @param staff
     */
    public static void displayConsole(Staff staff) {
        clearScreen();
        String prompt = "[" + staff.getUsername() + " @ " + staff.getJob() + "]:>_ ";
        System.out.println("Welcome, '" + staff.getFirstName() + " " + staff.getLastName() + "'!");
        System.out.println("-------------------------------------------------------------------------------");
        //displayAlert();
        do {
            prompt(prompt);
            Engine.execute(cin.nextLine());
        } while (true);
    }

    /**
     * @deprecated 
     * Display all task details
     * @param taskLs
     */
    public static void displayAllTasksDetail(List<Task> taskLs) {
        Iterator itr = taskLs.iterator();

        while (itr.hasNext()) {
            Task t = (Task) itr.next();
            System.out.print(t.getTaskId() + " " + t.getStatus() + " " + t.getTimeCreated());
        }
    }

    /**
     * Display task map
     * @param taskLs
     */
    public static void displayTaskMap(List<Task> taskLs) {
        System.out.println();
        System.out.println("o-------------------------------------------  Task Map ------------------------------------------------------------------------------------o");
        System.out.println("| ID |   Status   |       Type      | Urgency |       Location        |          Description          |  Time Reported  |   Time Closed    |");
        System.out.println("o------------------------------------------------------------------------------------------------------------------------------------------o");
        Iterator itr = taskLs.iterator();
        String timeReported = " - ";
        String timeClosed = " - ";

        while (itr.hasNext()) {
            Task t = (Task) itr.next();
            timeClosed = (t.getTimeClosed() != null) ? t.getTimeClosed().toString().substring(0, 16) : "       -       ";
            timeReported = (t.getTimeCreated() != null) ? t.getTimeCreated().toString().substring(0, 16) : "       -       ";
            System.out.println("|" + wrapText(t.getTaskId().toString(), 3)
                    + " " + wrapText(t.getStatus(), 13) + "   "
                    + wrapText(t.getType(), 15) + "    " + wrapText(t.getUrgency().toString(), 2) + "     "
                    + wrapText(t.getLocation(), 22) + "  " + wrapText(t.getDescription(), 30) + "   "
                    + timeReported + "  " + timeClosed);
        }
        System.out.println("o------------------------------------------------------------------------------------------------------------------------------------------o");
        System.out.println();
    }

    /**
     * Display Field Officer's current assigned task
     * @param t
     * @param hm
     */
    public static void displayFOAssignedTask(Task t, HashMap hm) {
        if (t == null) {
            System.out.println("---   You have no task assigned.   ---");
        } else {
            System.out.println("---   You are currently assigned to Task " + t.getTaskId() + "  ---");
            System.out.println("| Case Type   : " + t.getType());
            System.out.println("| Urgency     : " + t.getUrgency());
            System.out.println("| Description : " + t.getDescription());
            System.out.println("| Location    : " + t.getLocation());
            System.out.println("| --- Resources Allocated to the Task ---");
            displayHashMap(hm, "| ", "", "Error Occured");
            System.out.println("o-----------------------------------------------------------------------------o");
        }
    }

    /**
     * Display resource map
     * @param lstaff
     * @param lres
     */
    public static void displayResourceMap(List<Staff> lstaff, List<Resource> lres) {
        displayStaffList(lstaff);
        displayResourceList(lres);
    }

    /**
     * Wrapper for print()
     * @param prompt
     */
    public static void prompt(String prompt) {
        System.out.print(prompt);
    }

    /**
     * Wrapper for println()
     * @param prompt
     */
    public static void promptLn(String prompt) {
        System.out.println(prompt);
    }

    /**
     * Wrapper for showing invalid command
     */
    public static void displayInvalidCommand() {
        System.out.println("Invalid Command.");
    }

    /**
     * Utility to display message
     * @param msg
     * @param type
     */
    public static void displayMessage(List<String> msg, String type) {
        Iterator itr = msg.iterator();

        while (itr.hasNext()) {
            message = (String) itr.next();
            System.out.println(type + ": " + message);
        }
    }

    /**
     * Display Shutdown EMS message
     */
    public static void shutdown() {
        Engine.shutdown();
    }

    /**
     * Display create task dialogue
     */
    public static void displayCreateTask() {
        //HashMap taskHm = new HashMap<String, ArrayList<String>>();        
        try {
            List task = new ArrayList();
            String input = "";
            System.out.println("o-----------------------------------------------------------------------------o");
            System.out.println("|                            Create New Task                                  |");
            System.out.println("o-----------------------------------------------------------------------------o");

            // Ensure the type of case are valid
            /*
            String[] type = {"ROBBERY", "FIRE", "ACCIDENT", "TRAPPED", "FLOODING", "LANDSLIDE"};
            List<String> ltype = Arrays.asList(type);
            while (true) {
            System.out.print("Valid Type of Case : ");
            printList(ltype);
            System.out.print("| Enter type of case : ");
            input = cin.nextLine();
            if (ltype.contains(input.toUpperCase())) {
            task.add(0, input);
            break;
            } else {
            promptLn("WARNING: Invalid Case Type.");
            continue;
            }
            } */
            System.out.print("| Enter type of case : ");
            task.add(0, cin.nextLine());
            System.out.print("| Enter description of the case : ");
            task.add(1, cin.nextLine());
            System.out.print("| Enter location of the case : ");
            task.add(2, cin.nextLine());

            while (true) {
                System.out.print("| Enter urgency of the case (On the scale of 10, 10 most urgent) : ");
                input = cin.nextLine();
                if (!Engine.checkInt(input, 1, 10)) {
                    System.out.println("Please enter an integer between 1 and 10!");
                    continue;
                } else {
                    task.add(3, input);
                    break;
                }
            }

            task.add(4, getValidResourceInput("| Number of Police required: "));
            task.add(5, getValidResourceInput("| Number of Medic required: "));
            task.add(6, getValidResourceInput("| Number of Firefighter required: "));
            task.add(7, getValidResourceInput("| Number of Car required: "));
            task.add(8, getValidResourceInput("| Number of Medical Kit required: "));
            task.add(9, getValidResourceInput("| Number of Fire Engine required: "));
            System.out.println("-------------------");
            System.out.println("Processing data...");
            System.out.println("- Overall Task Detail -");
            System.out.println("| Type of case : " + task.get(0).toString());
            System.out.println("| Description of the case : " + task.get(1));
            System.out.println("| Location of the case : " + task.get(2));
            System.out.println("| Urgency : " + task.get(3));
            System.out.println("| Number of Police required: " + task.get(4));
            System.out.println("| Number of Medic  required: " + task.get(5));
            System.out.println("| Number of Firefighter required: " + task.get(6));
            System.out.println("| Number of Car required: " + task.get(7));
            System.out.println("| Number of Medical Kit required: " + task.get(8));
            System.out.println("| Number of Fire Engine required: " + task.get(9));
            System.out.println("-------------------");
            System.out.print("Do you want to create a task with the above detail? Enter 'y' or 'n': ");
            if (getDecision()) {
                Engine.processCreateTask(task);
                System.out.println("Task Created!");
            } else {
                System.out.println("Aborting task creation...");
            }
            System.out.println("o-----------------------------------------------------------------------------o");
        } catch (Exception e) {
            System.err.println(e);
        }
    }

    /**
     * Gets input from user about requested resources.
     * 
     */
    public static List<Integer> requestResources() {
        List<Integer> res = new ArrayList<Integer>();
        System.out.print("| Additional Police required: ");
        res.add(0, cin.nextInt());
        System.out.print("| Additional Medics required: ");
        res.add(1, cin.nextInt());
        System.out.print("| Additional Firefighters required: ");
        res.add(2, cin.nextInt());
        System.out.print("| Additional Cars required: ");
        res.add(3, cin.nextInt());
        System.out.print("| Additional Medical Kits required: ");
        res.add(4, cin.nextInt());
        System.out.print("| Additional Fire Engine required: ");
        res.add(5, cin.nextInt());
        return res;
    }

    /**
     * Display manual dispatch screen for dispatcher
     * @param ltask
     * @param hm
     * @param lres
     * @param lstaff
     */
    public static void displayDispatchTask(List<Task> ltask, HashMap hm, List<Resource> lres, List<Staff> lstaff) {
        List<Integer> lResId = new ArrayList<Integer>();
        displayTaskListDetail(ltask, hm, null, null);

        System.out.println("==== Assignment ====");
        boolean isBreak = false;
        boolean canProcess = true;
        String input, resType = "";
        //HashMap<String, String> addedRes = new HashMap<String, String>();
        Iterator iter = hm.entrySet().iterator();

        while (iter.hasNext() && isBreak == false) {
            Map.Entry mEntry = (Map.Entry) iter.next();
            for (int i = 1; i <= Integer.parseInt(mEntry.getValue().toString()); i++) {
                while (isBreak == false) {
                    resType = mEntry.getKey().toString();
                    displayFilteredResourceList(lres, resType);
                    System.out.print("Assign " + resType + " (" + i + ") : ");
                    input = cin.nextLine();
                    if (input.equals("cancel")) {
                        isBreak = true;
                        canProcess = false;
                        promptLn("Dispatching operation cancelled successfully!");
                        break;
                    } else if (!Engine.checkInt(input)) {
                        promptLn("ERROR: " + input + " is not a valid number.");
                        continue;
                    } else if (!isResExist(lres, resType, input)) {
                        promptLn("ERROR: " + input + " is not a valid resource ID for " + resType + ".");
                        continue;
                    } else {
                        // remove added res from current resource list 
                        //and add it to the addedRes list for later display confirmation
                        //addedRes.put(resType, );
                        removeAssignedRes(lres, input);
                        //dump in to list
                        lResId.add(Integer.parseInt(input));
                        break;
                    }
                }
            }
        }

        if (canProcess) {
            // Display Field Officer List
            do {
                displayFOList(lstaff);
                System.out.print("\nAssign Field Officer: ");
                input = cin.nextLine();
                if (input.equals("cancel")) {
                    promptLn("Dispatching operation cancelled successfully!");
                    break;
                } else if (!Engine.checkInt(input)) {
                    promptLn("ERROR: " + input + " is not a valid number.");
                    continue;
                } else if (!isFOExist(lstaff, input)) {
                    promptLn("ERROR: " + input + " is not a valid Field Officer ID.");
                    continue;
                } else {
                    // process                    
                    if (Engine.processDispatchTask(ltask.get(0).getTaskId(), Integer.parseInt(input), lResId)) {
                        //displayHashMap(addedRes, "| ", "Error Occurred!");
                        promptLn("o------------------------------------------------o");
                    }
                    break;
                }
            } while (true);
        }
    }

    /**
     * Check whether resource exist in the list (memory)
     * @param lres
     * @param filter
     * @param id
     * @return true or false
     */
    public static boolean isResExist(List<Resource> lres, String filter, String id) {
        boolean isExist = false;
        for (Iterator<Resource> iter = lres.iterator(); iter.hasNext();) {
            Resource s = iter.next();
            if (s.getId() == Integer.parseInt(id) && s.getName().equals(filter)) {
                return isExist = true;
            }
        }
        return isExist;
    }

    /**
     * Check whether Field Officer exist in the list (memory)
     * @param lstaff
     * @param id
     * @return true of false
     */
    public static boolean isFOExist(List<Staff> lstaff, String id) {
        boolean isExist = false;
        for (Iterator<Staff> iter = lstaff.iterator(); iter.hasNext();) {
            Staff s = iter.next();
            if (s.getStaffId() == Integer.parseInt(id)) {
                return isExist = true;
            }
        }
        return isExist;
    }

    /**
     * Remove resource from current list in memory
     * @param lres
     * @param filter
     * @return value of removed resource
     */
    public static String removeAssignedRes(List<Resource> lres, String filter) {
        String resValue = "";
        for (Iterator<Resource> iter = lres.iterator(); iter.hasNext();) {
            Resource s = iter.next();
            if (s.getId() == Integer.parseInt(filter)) {
                resValue = "[" + s.getId() + "] " + s.getName() + " " + s.getNumber();
                iter.remove();
                break;
            }
        }
        return resValue;
    }

    /**
     * Utility to display hashmap
     * @param hm
     * @param append
     * @param error
     */
    public static void displayHashMap(HashMap hm, String append, String error) {
        if (!hm.isEmpty()) {
            Iterator iter = hm.entrySet().iterator();

            while (iter.hasNext()) {
                Map.Entry mEntry = (Map.Entry) iter.next();
                System.out.println(append + mEntry.getKey() + " : " + mEntry.getValue());
            }
        } else {
            System.out.println(error);
        }
    }

    /**
     * Utility to display hashmap 2
     * @param hm
     * @param append
     * @param separator
     * @param error
     */
    public static void displayHashMap(HashMap hm, String append, String separator, String error) {
        if (!hm.isEmpty()) {
            Iterator iter = hm.entrySet().iterator();

            while (iter.hasNext()) {
                Map.Entry mEntry = (Map.Entry) iter.next();
                System.out.println(append + mEntry.getKey() + separator + mEntry.getValue());
            }
        } else {
            System.out.println(error);
        }
    }

    /**
     * @deprecated
     * Display task list
     * @param ltask
     */
    public static void displayTaskList(List<Task> ltask) {
        System.out.println();

        Iterator itr = ltask.iterator();
        while (itr.hasNext()) {
            Task t = (Task) itr.next();
            System.out.println(t.getTaskId() + "\t" + t.getStatus() + "\t"
                    + t.getType() + "\t" + t.getLocation() + "\t" + t.getDescription());
        }
        System.out.println();
    }

    /**
     * Display full detail of a task
     * @param ltask
     * @param hm
     * @param hm2
     * @param ls
     */
    public static void displayTaskListDetail(List<Task> ltask, HashMap hm, HashMap hm2, List<Staff> ls) {
        System.out.println();

        Iterator itr = ltask.iterator();
        while (itr.hasNext()) {
            Task t = (Task) itr.next();
            System.out.println("o--- TaskID: " + t.getTaskId() + "---o");
            System.out.println("| Status      : " + t.getStatus());
            System.out.println("| Urgency     : " + t.getUrgency());
            System.out.println("| Case Type   : " + t.getType());
            System.out.println("| Description : " + t.getDescription());
            System.out.println("| Location    : " + t.getLocation());
            System.out.println("|");
            if (t.getStatus().equals("CLOSED")) {
                displayHashMap(hm, "| ", "| Task has already been CLOSED.");
            } else if (t.getStatus().equals("UNDISPATCHED")) {
                System.out.println("| --- Resource Required: ---");
                displayHashMap(hm, "| ", "| No Resource Allocated to Task yet");
            } else if (t.getStatus().equals("DISPATCHED")) {
                System.out.println("| --- Resource Allocated: ---");
                displayHashMap(hm2, "| ", "| No Resource Allocated to Task yet");
                System.out.println("| --- Field Officer In-Charge ---");
                if (ls != null) {
                    System.out.println("| " + ls.get(0).getFirstName() + " " + ls.get(0).getLastName()
                            + ": [ID " + ls.get(0).getStaffId() + "]");
                }
            }
            System.out.println("o-----------------------------------------------------------------------------o");
        }
        System.out.println();
    }

    /**
     * Display resource list
     * @param lres
     */
    public static void displayResourceList(List<Resource> lres) {
        if (!lres.isEmpty()) {
            System.out.println();
            System.out.println("o---------------------------- RESOURCE ---------------------------------------o");
            System.out.println("| ID |      Status     |        Type         |          Serial Number         |");
            System.out.println("o-----------------------------------------------------------------------------o");

            Iterator itr2 = lres.iterator();

            while (itr2.hasNext()) {
                Resource r = (Resource) itr2.next();
                System.out.println("| " + expandSpace(Integer.toString(r.getId()), 3)
                        + " " + expandSpace(r.getStatus(), 13) + "\t"
                        + expandSpace(r.getName(), 13) + "\t\t" + r.getNumber() + "\t");
            }
            System.out.println("o-----------------------------------------------------------------------------o");
            System.out.println();
        } else {
            System.out.println("There is no Resources available");
        }
    }

    /**
     * Display resource list with filter on resource type
     * @param lres
     * @param filter
     */
    public static void displayFilteredResourceList(List<Resource> lres, String filter) {
        if (!lres.isEmpty()) {
            System.out.println();
            Iterator itr = lres.iterator();

            while (itr.hasNext()) {
                Resource r = (Resource) itr.next();
                if (r.getName().equalsIgnoreCase(filter)) {
                    System.out.println(r.getId() + "\t" + r.getStatus() + "\t"
                            + r.getName() + " " + r.getNumber());
                }
            }
            System.out.println();
        } else {
            System.out.println("There is no matching " + filter + " resources.");
        }
    }

    /**
     * Gets y/n input from user and return input in boolean for
     * @return yes = true, no = false
     */
    public static boolean getDecision() {
        String in;
        while (true) {
            in = cin.nextLine();
            if (in.equalsIgnoreCase("y")) {
                return true;
            }
            if (in.equalsIgnoreCase("n")) {
                return false;
            }
            prompt("Enter 'y' or 'n'");
        }
    }

    /**
     * Prints the manual/help screen for Field officers
     */
    public static void displayFOHelp() {
        System.out.println("--- Commands for Field Officer  ---");
        System.out.println("help    - Print command list");
        System.out.println("view    - view currently assigned task");
        System.out.println("report  - Report the completion of current task");
        System.out.println("request - [Unavailable] Request additional resources for current task");
        System.out.println("logout  - Logouts and returns to login screen");
        System.out.println("quit    - Shuts EMS down");
    }

    /**
     * Prints the manual/help screen for Call Taker
     */
    public static void displayCTHelp() {
        System.out.println("--- Commands for Call Taker  ---");
        System.out.println("help    - Print command list");
        System.out.println("create  - Create a new task");
        System.out.println("taskmap - View the task list");
        System.out.println("logout  - Logouts and returns to login screen");
        System.out.println("quit    - Shuts EMS down");
    }

    /**
     * Prints the manual/help screen for Dispatcher
     */
    public static void displayDPHelp() {
        System.out.println("--- Commands for Dispatcher  ---");
        System.out.println("help    - Print command list");
        System.out.println("taskmap                - View the task list");
        System.out.println("taskmap <arg>          - View the filtered task list based on status."
                + "Valid arguments are 'undispatched', 'dispatched', 'closed'");
        System.out.println("resourcemap - View the list of resources");
        System.out.println("resourcemap <arg>       - View the list of resources with filter for status."
                + "Valid arguments are 'av' (available) or 'unav' (unavailable) ");
        System.out.println("view <taskid>           - View details for a task");
        System.out.println("dispatch <taskid>       - Manual assign resources to the task");
        System.out.println("autodispatch <taskid>   - Automatic assign resources to the task");
        System.out.println("logout  - Logouts and returns to login screen");
        System.out.println("quit    - Shuts EMS down");
    }

    /**
     * Display staff list 
     * @param lstaff
     */
    public static void displayStaffList(List<Staff> lstaff) {
        if (!lstaff.isEmpty()) {
            System.out.println();
            System.out.println("o--------------------------- FIELD OFFICER -----------------------------------o");
            System.out.println("| ID |       Status       |                         Name                      |");
            System.out.println("o-----------------------------------------------------------------------------o");

            Iterator itr = lstaff.iterator();
            while (itr.hasNext()) {
                Staff s = (Staff) itr.next();
                System.out.println("| " + expandSpace(s.getStaffId().toString(), 3)
                        + " " + expandSpace(s.getStatus(), 13) + "   \t"
                        + s.getFirstName() + " " + s.getLastName());
            }
            System.out.println("o-----------------------------------------------------------------------------o");
            System.out.println();
        } else {
            System.out.println("There is no Field Officer available");
        }
    }

    /**
     * Display Field Officer List
     * @param lstaff
     */
    public static void displayFOList(List<Staff> lstaff) {
        System.out.println();
        Iterator itr = lstaff.iterator();
        while (itr.hasNext()) {
            Staff s = (Staff) itr.next();
            System.out.println(s.getStaffId() + "\t" + s.getStatus() + "\t"
                    + s.getFirstName() + " " + s.getLastName());
        }
        System.out.println();
    }

    /**
     * Utility to print a list
     * @param ls
     */
    public static void printList(List ls) {
        Iterator itr = ls.iterator();
        while (itr.hasNext()) {
            System.out.print(itr.next().toString() + ", ");
        }
        System.out.println();
    }

    /**
     * Check for valid resource id from user input
     * @param prompt
     * @return user input
     */
    public static String getValidResourceInput(String prompt) {
        String input = "";
        while (true) {
            System.out.print(prompt);
            input = cin.nextLine();
            if (!Engine.checkInt(input)) {
                System.out.println("ERROR: Please enter a positive integer!");
                continue;
            } else {
                return input;
            }
        }
    }

    /**
     * Utility to expand space to fit available width
     * @param string
     * @param maxLength
     * @return formatted string
     */
    private static String expandSpace(String string, int maxLength) {
        if (string.length() >= maxLength) {
            return string;
        }

        int length = maxLength - string.length();
        char[] asChars = new char[length];
        Arrays.fill(asChars, 0, length, ' ');
        return new String(asChars) + string;
    }

    /*
    Wraps a string so that it fits a desired space.
     * Aligns to the right and truncates the end with ...
     * @param availableWidth
     * :<BR> Length of available space
     * @param s
     * :<BR> The Raw String
     * @return
     */
    private static String wrapText(String s, int availableWidth) {
        if (s.length() > availableWidth) {
            s = s.substring(0, (availableWidth - 3));
            s = s + "...";
        } else if (s.length() < availableWidth) {
            int sLen = availableWidth - s.length();
            String newS = "";
            for (int i = 0; i < sLen; i++) {
                newS = newS + " ";
            }
            newS = newS + s;
            s = newS;
        }
        return s;
    }
}
